package misc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import util.ArrayList;
import util.HashMap;
import util.Iterator;
import util.List;
import engine.GameEngine;
import engine.data.City;
import engine.data.Map;
import engine.data.Player;
import engine.data.Terrain;
import engine.data.Tile;
import engine.data.Unit;

public final class PersistenceTools {

	private PersistenceTools() {
		// no constructor
	}

	public static short[] resurrectShortArray(final DataInputStream din) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			short[] objects = new short[size];
			for (short i = 0; i < size; i++) {
				objects[i] = din.readShort();
			}
			return objects;
		}
		return null;
	}

	public static void persistShortArray(final short[] objects, final DataOutputStream dout) throws IOException {
		if (objects != null) {
			dout.writeShort(objects.length);
			for (short i = 0; i < objects.length; i++)
				dout.writeShort(objects[i]);
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static List resurrectUnitList(final DataInputStream din, final boolean complete) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			List objects = new ArrayList(size);
			for (short i = 0; i < size; i++)
				objects.add(resurrectUnit(din, complete));
			return objects;
		}
		return null;
	}

	public static void linkUnitList(final Player player) {
		for (short i = 0; i < player.units.size(); i++)
			((Unit) player.units.get(i)).player = player;
	}

	public static void linkCityList(final Player player) {
		for (short c = 0; c < player.cities.size(); c++) {
			City city = (City) player.cities.get(c);
			city.player = player;
			city.tile = GameEngine.map.getTile(city.x, city.y);
			city.initTiles(GameEngine.map);
		}
	}

	public static List resurrectCityList(final DataInputStream din, final boolean complete) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			List objects = new ArrayList(size);
			for (short i = 0; i < size; i++)
				objects.add(PersistenceTools.resurrectCity(din, complete));
			return objects;
		}
		return null;
	}

	public static List resurrectPlayerList(final DataInputStream din, final boolean complete) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			List objects = new ArrayList(size);
			for (short i = 0; i < size; i++) {
				objects.add(PersistenceTools.resurrectPlayer(din, complete));
			}
			return objects;
		}
		return null;
	}

	public static void persistList(final List objects, final DataOutputStream dout, final boolean complete)
			throws IOException {
		if (objects != null) {
			dout.writeShort(objects.size());
			for (short i = 0; i < objects.size(); i++) {
				dout.writeShort(((Persistent) objects.get(i)).getId());
				((Persistent) objects.get(i)).persist(dout, complete);
			}
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static List resurrectByteList(final DataInputStream din) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			List objects = new ArrayList(size);
			for (short i = 0; i < size; i++)
				objects.add(new Byte(din.readByte()));
			return objects;
		}
		return null;
	}

	public static void persistByteList(final List objects, final DataOutputStream dout) throws IOException {
		if (objects != null) {
			dout.writeShort(objects.size());
			for (short i = 0; i < objects.size(); i++)
				dout.writeByte(((Byte) objects.get(i)).byteValue());
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static util.Map resurrectByteMap(final DataInputStream din) throws IOException {
		short size = din.readShort();
		if (size != Persistent.NULL) {
			util.Map objects = new HashMap(size);
			for (short i = 0; i < size; i++)
				objects.put(new Byte(din.readByte()), new Byte(din.readByte()));
			return objects;
		}
		return null;
	}

	public static void persistByteMap(final util.Map objects, final DataOutputStream dout) throws IOException {
		if (objects != null) {
			dout.writeShort(objects.size());
			Iterator i = objects.keySet().iterator();
			while (i.hasNext()) {
				Byte key = (Byte) i.next();
				dout.writeByte(key.byteValue());
				dout.writeByte(((Byte) objects.get(key)).byteValue());
			}
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static Tile[][] resurrectTileArray(final DataInputStream din, final boolean complete) throws IOException {
		short lengthI = din.readShort();
		if (lengthI != Persistent.NULL) {
			short lengthJ = din.readShort();
			Tile[][] objects = new Tile[lengthI][lengthJ];
			for (short i = 0; i < lengthI; i++)
				for (short j = 0; j < lengthJ; j++) {
					objects[i][j] = resurrectTile(din, complete);
				}
			return objects;
		}
		return null;
	}

	public static void persistTileArray(final Tile[][] objects, final DataOutputStream dout, final boolean complete)
			throws IOException {
		if (objects != null) {
			dout.writeShort(objects.length);
			dout.writeShort(objects[0].length);
			for (short i = 0; i < objects.length; i++)
				for (short j = 0; j < objects[0].length; j++)
					persist(objects[i][j], dout, complete);
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static boolean[][] resurrectBooleanArray2(final DataInputStream din) throws IOException {
		short lengthI = din.readShort();
		if (lengthI != Persistent.NULL) {
			short lengthJ = din.readShort();
			boolean[][] objects = new boolean[lengthI][lengthJ];
			for (short i = 0; i < lengthI; i++)
				for (short j = 0; j < lengthJ; j++) {
					objects[i][j] = din.readBoolean();
				}
			return objects;
		}
		return null;
	}

	public static void persistBooleanArray2(final boolean[][] objects, final DataOutputStream dout) throws IOException {
		if (objects != null) {
			dout.writeShort(objects.length);
			dout.writeShort(objects[0].length);
			for (short i = 0; i < objects.length; i++)
				for (short j = 0; j < objects[0].length; j++)
					dout.writeBoolean(objects[i][j]);
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static boolean[] resurrectBooleanArray(final DataInputStream din) throws IOException {
		short lengthI = din.readShort();
		if (lengthI != Persistent.NULL) {
			boolean[] objects = new boolean[lengthI];
			for (short i = 0; i < lengthI; i++)
				objects[i] = din.readBoolean();
			return objects;
		}
		return null;
	}

	public static void persistBooleanArray(final boolean[] objects, final DataOutputStream dout) throws IOException {
		if (objects != null) {
			dout.writeShort(objects.length);
			for (short i = 0; i < objects.length; i++)
				dout.writeBoolean(objects[i]);
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static City resurrectCity(final DataInputStream din, final boolean complete) throws IOException {
		City city = new City();
		city.setId(din.readShort());
		city.resurrect(din, complete);
		short s = city.getId();
		if (s != Persistent.NULL) {
			if (!complete) {
				city = null;
				for (short p = 0; p < GameEngine.playerCount && (city == null || city.getId() != s); p++) {
					List cities = ((Player) GameEngine.players.get(p)).cities;
					if (cities != null)
						for (short c = 0; c < cities.size() && (city == null || city.getId() != s); c++)
							city = ((City) cities.get(c));
				}
			}
			return city;
		}
		return null;
	}

	public static Terrain resurrectTerrain(final DataInputStream din, final boolean complete) throws IOException {
		Terrain terrain = new Terrain();
		terrain.setId(din.readShort());
		terrain.resurrect(din, complete);
		short s = terrain.getId();
		if (s != Persistent.NULL) {
			if (!complete)
				terrain = GameEngine.map.getTerrain((short) (s / GameEngine.map.width),
						(short) (s % GameEngine.map.width));
			return terrain;
		}
		return null;
	}

	public static Unit resurrectUnit(final DataInputStream din, final boolean complete) throws IOException {
		Unit unit = new Unit();
		unit.setId(din.readShort());
		unit.resurrect(din, complete);
		short s = unit.getId();
		if (s != Persistent.NULL) {
			if (!complete) {
				unit = null;
				for (short p = 0; p < GameEngine.playerCount && (unit == null || unit.getId() != s); p++) {
					List units = ((Player) GameEngine.players.get(p)).units;
					if (units != null)
						for (short c = 0; c < units.size() && (unit == null || unit.getId() != s); c++)
							unit = ((Unit) units.get(c));
				}
			}
			return unit;
		}
		return null;
	}

	public static Player resurrectPlayer(final DataInputStream din, final boolean complete) throws IOException {
		Player player = new Player();
		player.setId(din.readShort());
		player.resurrect(din, complete);
		short s = player.getId();
		if (s != Persistent.NULL) {
			if (!complete) {
				player = null;
				for (short p = 0; p < GameEngine.playerCount && (player == null || player.getId() != s); p++)
					player = ((Player) GameEngine.players.get(p));
			}
			return player;
		}
		return null;
	}

	public static void linkGameEngine() {
		for (short p = 0; p < GameEngine.playerCount; p++)
			((Player) GameEngine.players.get(p)).linkPlayer();
	}

	public static Map resurrectMap(final DataInputStream din, final boolean complete) throws IOException {
		Map map = new Map();
		map.setId(din.readShort());
		map.resurrect(din, complete);
		short s = map.getId();
		if (s != Persistent.NULL) {
			if (!complete)
				return GameEngine.map;
			return map;
		}
		return null;
	}

	public static Tile resurrectTile(final DataInputStream din, final boolean complete) throws IOException {
		Tile tile = new Tile();
		tile.setId(din.readShort());
		tile.resurrect(din, complete);
		short s = tile.getId();
		if (s != Persistent.NULL) {
			if (!complete)
				tile = GameEngine.map.getTile((short) (s / GameEngine.map.width), (short) (s % GameEngine.map.width));
			return tile;
		}
		return null;
	}

	public static void persist(final Persistent object, final DataOutputStream dout, final boolean complete)
			throws IOException {
		if (object != null) {
			dout.writeShort(object.getId());
			object.persist(dout, complete);
		} else
			dout.writeShort(Persistent.NULL);
	}

	public static ByteArrayOutputStream saveGameEngine() throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dout = new DataOutputStream(baos);
		GameEngine.persist(dout);
		dout.flush();
		return baos;
	}

	public static ByteArrayOutputStream save(final Persistent persistent, final boolean complete) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dout = new DataOutputStream(baos);
		persistent.persist(dout, complete);
		dout.flush();
		return baos;
	}

	public static void loadGameEngine(final ByteArrayInputStream bais) throws IOException {
		DataInputStream din = new DataInputStream(bais);
		GameEngine.resurrect(din);
	}

	public static void load(final ByteArrayInputStream bais, final Persistent persistent, final boolean complete)
			throws IOException {
		DataInputStream din = new DataInputStream(bais);
		persistent.resurrect(din, complete);
	}
}
